//package com.xyoye.common_component.network.helper;
//
//import static java.net.HttpURLConnection.HTTP_CLIENT_TIMEOUT;
//import static java.net.HttpURLConnection.HTTP_MOVED_PERM;
//import static java.net.HttpURLConnection.HTTP_MOVED_TEMP;
//import static java.net.HttpURLConnection.HTTP_MULT_CHOICE;
//import static java.net.HttpURLConnection.HTTP_PROXY_AUTH;
//import static java.net.HttpURLConnection.HTTP_SEE_OTHER;
//import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
//import static java.net.HttpURLConnection.HTTP_UNAVAILABLE;
//import static okhttp3.internal.Util.closeQuietly;
//import static okhttp3.internal.http.StatusLine.HTTP_PERM_REDIRECT;
//import static okhttp3.internal.http.StatusLine.HTTP_TEMP_REDIRECT;
//
//import androidx.annotation.Nullable;
//
//import java.io.FileNotFoundException;
//import java.io.IOException;
//import java.io.InterruptedIOException;
//import java.net.ProtocolException;
//import java.net.Proxy;
//import java.net.SocketTimeoutException;
//import java.security.cert.CertificateException;
//
//import javax.net.ssl.SSLHandshakeException;
//import javax.net.ssl.SSLPeerUnverifiedException;
//
//import okhttp3.HttpUrl;
//import okhttp3.Interceptor;
//import okhttp3.OkHttpClient;
//import okhttp3.Request;
//import okhttp3.RequestBody;
//import okhttp3.Response;
//import okhttp3.Route;
//import okhttp3.internal.Internal;
//import okhttp3.internal.connection.Exchange;
//import okhttp3.internal.connection.RouteException;
//import okhttp3.internal.http.HttpMethod;
//import okhttp3.internal.http.RealInterceptorChain;
//import okhttp3.internal.http2.ConnectionShutdownException;
//
///**
// * This interceptor recovers from failures and follows redirects as necessary. It may throw an
// * {@link IOException} if the call was canceled.
// * <p>
// * 自定义重定向拦截器，用于重定向时可选的移除"Authorization"
// */
//public final class CustomRetryAndFollowUpInterceptor implements Interceptor {
//    /**
//     * How many redirects and auth challenges should we attempt? Chrome follows 21 redirects; Firefox,
//     * curl, and wget follow 20; Safari follows 16; and HTTP/1.0 recommends 5.
//     */
//    private static final int MAX_FOLLOW_UPS = 20;
//
//    private final OkHttpClient client;
//    private final boolean followSslRedirects;
//    private final boolean followRedirects;
//    private final boolean retryOnConnectionFailure;
//    private final boolean removeAuthorization;
//
//    public CustomRetryAndFollowUpInterceptor(OkHttpClient client) {
//        this(client, true, false, true, false);
//    }
//
//    public CustomRetryAndFollowUpInterceptor(
//            OkHttpClient client,
//            boolean followRedirects,
//            boolean followSslRedirects,
//            boolean retryOnConnectionFailure,
//            boolean removeAuthorization
//    ) {
//        this.client = client;
//        this.followRedirects = followRedirects;
//        this.followSslRedirects = followSslRedirects;
//        this.retryOnConnectionFailure = retryOnConnectionFailure;
//        this.removeAuthorization = removeAuthorization;
//    }
//
//    @Override
//    public Response intercept(Chain chain) throws IOException {
//        Request request = chain.request();
//        RealInterceptorChain realChain = (RealInterceptorChain) chain;
//        Transmitter transmitter = realChain.transmitter();
//
//        int followUpCount = 0;
//        Response priorResponse = null;
//        while (true) {
//            transmitter.prepareToConnect(request);
//
//            if (transmitter.isCanceled()) {
//                throw new IOException("Canceled");
//            }
//
//            Response response;
//            boolean success = false;
//            try {
//                response = realChain.proceed(request, transmitter, null);
//                success = true;
//            } catch (RouteException e) {
//                // The attempt to connect via a route failed. The request will not have been sent.
//                if (!recover(e.getLastConnectException(), transmitter, false, request)) {
//                    throw e.getFirstConnectException();
//                }
//                continue;
//            } catch (IOException e) {
//                // An attempt to communicate with a server failed. The request may have been sent.
//                boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
//                if (!recover(e, transmitter, requestSendStarted, request)) throw e;
//                continue;
//            } finally {
//                // The network call threw an exception. Release any resources.
//                if (!success) {
//                    transmitter.exchangeDoneDueToException();
//                }
//            }
//
//            // Attach the prior response if it exists. Such responses never have a body.
//            if (priorResponse != null) {
//                response = response.newBuilder()
//                        .priorResponse(priorResponse.newBuilder()
//                                .body(null)
//                                .build())
//                        .build();
//            }
//
//            Exchange exchange = Internal.instance.exchange(response);
//            Route route = exchange != null ? exchange.connection().route() : null;
//            Request followUp = followUpRequest(response, route);
//
//            if (followUp == null) {
//                if (exchange != null && exchange.isDuplex()) {
//                    transmitter.timeoutEarlyExit();
//                }
//                return response;
//            }
//
//            RequestBody followUpBody = followUp.body();
//            if (followUpBody != null && followUpBody.isOneShot()) {
//                return response;
//            }
//
//            closeQuietly(response.body());
//            if (transmitter.hasExchange()) {
//                exchange.detachWithViolence();
//            }
//
//            if (++followUpCount > MAX_FOLLOW_UPS) {
//                throw new ProtocolException("Too many follow-up requests: " + followUpCount);
//            }
//
//            request = followUp;
//            priorResponse = response;
//        }
//    }
//
//    /**
//     * Report and attempt to recover from a failure to communicate with a server. Returns true if
//     * {@code e} is recoverable, or false if the failure is permanent. Requests with a body can only
//     * be recovered if the body is buffered or if the failure occurred before the request has been
//     * sent.
//     */
//    private boolean recover(IOException e, Transmitter transmitter,
//                            boolean requestSendStarted, Request userRequest) {
//        // The application layer has forbidden retries.
//        if (!retryOnConnectionFailure) return false;
//
//        // We can't send the request body again.
//        if (requestSendStarted && requestIsOneShot(e, userRequest)) return false;
//
//        // This exception is fatal.
//        if (!isRecoverable(e, requestSendStarted)) return false;
//
//        // No more routes to attempt.
//        if (!transmitter.canRetry()) return false;
//
//        // For failure recovery, use the same route selector with a new connection.
//        return true;
//    }
//
//    private boolean requestIsOneShot(IOException e, Request userRequest) {
//        RequestBody requestBody = userRequest.body();
//        return (requestBody != null && requestBody.isOneShot())
//                || e instanceof FileNotFoundException;
//    }
//
//    private boolean isRecoverable(IOException e, boolean requestSendStarted) {
//        // If there was a protocol problem, don't recover.
//        if (e instanceof ProtocolException) {
//            return false;
//        }
//
//        // If there was an interruption don't recover, but if there was a timeout connecting to a route
//        // we should try the next route (if there is one).
//        if (e instanceof InterruptedIOException) {
//            return e instanceof SocketTimeoutException && !requestSendStarted;
//        }
//
//        // Look for known client-side or negotiation errors that are unlikely to be fixed by trying
//        // again with a different route.
//        if (e instanceof SSLHandshakeException) {
//            // If the problem was a CertificateException from the X509TrustManager,
//            // do not retry.
//            if (e.getCause() instanceof CertificateException) {
//                return false;
//            }
//        }
//        if (e instanceof SSLPeerUnverifiedException) {
//            // e.g. a certificate pinning error.
//            return false;
//        }
//
//        // An example of one we might want to retry with a different route is a problem connecting to a
//        // proxy and would manifest as a standard IOException. Unless it is one we know we should not
//        // retry, we return true and try a new route.
//        return true;
//    }
//
//    /**
//     * Figures out the HTTP request to make in response to receiving {@code userResponse}. This will
//     * either add authentication headers, follow redirects or handle a client request timeout. If a
//     * follow-up is either unnecessary or not applicable, this returns null.
//     */
//    private Request followUpRequest(Response userResponse, @Nullable Route route) throws IOException {
//        if (userResponse == null) throw new IllegalStateException();
//        int responseCode = userResponse.code();
//
//        final String method = userResponse.request().method();
//        switch (responseCode) {
//            case HTTP_PROXY_AUTH:
//                Proxy selectedProxy = route != null
//                        ? route.proxy()
//                        : client.proxy();
//                if (selectedProxy.type() != Proxy.Type.HTTP) {
//                    throw new ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy");
//                }
//                return client.proxyAuthenticator().authenticate(route, userResponse);
//
//            case HTTP_UNAUTHORIZED:
//                return client.authenticator().authenticate(route, userResponse);
//
//            case HTTP_PERM_REDIRECT:
//            case HTTP_TEMP_REDIRECT:
//                // "If the 307 or 308 status code is received in response to a request other than GET
//                // or HEAD, the user agent MUST NOT automatically redirect the request"
//                if (!method.equals("GET") && !method.equals("HEAD")) {
//                    return null;
//                }
//                // fall-through
//            case HTTP_MULT_CHOICE:
//            case HTTP_MOVED_PERM:
//            case HTTP_MOVED_TEMP:
//            case HTTP_SEE_OTHER:
//                // Does the client allow redirects?
//                if (!followRedirects) return null;
//
//                String location = userResponse.header("Location");
//                if (location == null) return null;
//                HttpUrl url = userResponse.request().url().resolve(location);
//
//                // Don't follow redirects to unsupported protocols.
//                if (url == null) return null;
//
//                // If configured, don't follow redirects between SSL and non-SSL.
//                boolean sameScheme = url.scheme().equals(userResponse.request().url().scheme());
//                if (!sameScheme && !followSslRedirects) {
//                    return null;
//                }
//
//
//                // Most redirects don't include a request body.
//                Request.Builder requestBuilder = userResponse.request().newBuilder();
//                if (HttpMethod.permitsRequestBody(method)) {
//                    final boolean maintainBody = HttpMethod.redirectsWithBody(method);
//                    if (HttpMethod.redirectsToGet(method)) {
//                        requestBuilder.method("GET", null);
//                    } else {
//                        RequestBody requestBody = maintainBody ? userResponse.request().body() : null;
//                        requestBuilder.method(method, requestBody);
//                    }
//                    if (!maintainBody) {
//                        requestBuilder.removeHeader("Transfer-Encoding");
//                        requestBuilder.removeHeader("Content-Length");
//                        requestBuilder.removeHeader("Content-Type");
//                    }
//                }
//
//                // When redirecting across hosts, drop all authentication headers. This
//                // is potentially annoying to the application layer since they have no
//                // way to retain them.
//                if (removeAuthorization && !sameConnection(userResponse.request().url(), url)) {
//                    requestBuilder.removeHeader("Authorization");
//                }
//
//
//                return requestBuilder.url(url).build();
//
//            case HTTP_CLIENT_TIMEOUT:
//                // 408's are rare in practice, but some servers like HAProxy use this response code. The
//                // spec says that we may repeat the request without modifications. Modern browsers also
//                // repeat the request (even non-idempotent ones.)
//
//                if (!retryOnConnectionFailure) {
//                    // The application layer has directed us not to retry the request.
//                    return null;
//                }
//
//
//                RequestBody requestBody = userResponse.request().body();
//                if (requestBody != null && requestBody.isOneShot()) {
//                    return null;
//                }
//
//                if (userResponse.priorResponse() != null
//                        && userResponse.priorResponse().code() == HTTP_CLIENT_TIMEOUT) {
//                    // We attempted to retry and got another timeout. Give up.
//                    return null;
//                }
//
//                if (retryAfter(userResponse, 0) > 0) {
//                    return null;
//                }
//
//                return userResponse.request();
//
//            case HTTP_UNAVAILABLE:
//                if (userResponse.priorResponse() != null
//                        && userResponse.priorResponse().code() == HTTP_UNAVAILABLE) {
//                    // We attempted to retry and got another timeout. Give up.
//                    return null;
//                }
//
//                if (retryAfter(userResponse, Integer.MAX_VALUE) == 0) {
//                    // specifically received an instruction to retry without delay
//                    return userResponse.request();
//                }
//
//                return null;
//
//            default:
//                return null;
//        }
//    }
//
//    private int retryAfter(Response userResponse, int defaultDelay) {
//        String header = userResponse.header("Retry-After");
//
//        if (header == null) {
//            return defaultDelay;
//        }
//
//        // https://tools.ietf.org/html/rfc7231#section-7.1.3
//        // currently ignores a HTTP-date, and assumes any non int 0 is a delay
//        if (header.matches("\\d+")) {
//            return Integer.valueOf(header);
//        }
//
//        return Integer.MAX_VALUE;
//    }
//}